iT邦幫忙

2022 iThome 鐵人賽

DAY 23
1

Day23 自己做一個價值幾十萬的動態網站

第二十三課:SearchItem搜尋欄filter真實資料的實作 query得各種混合應用

github Day21~23完成的連結

前幾天我們都了解並完成了context的部分,現在要開始進到後面的大重點,將我們的searchBar啟用,可以用它來查詢我們的飯店資料,這個熟悉後,後台的所有資料爬梳都可以應用在這個概念上。
並今天我們要來完成我們的SearchItem搜尋欄,讓他條件搜尋是可以串接到我們的後端資料庫抓出我們想要的資料。

query得各種混合應用

我們之前學到Api中的我們可以利用url的改變來查詢我們想要的資料,包括了city,type到是不是popularHotels熱門排行。

城市搜尋與最高價格、最低價格搜尋Api

為了完成我們的SearchItem搜尋欄,我們要同時具備三種filter功能,城市、最低價格與最高價格,這邊特別注意入住時間與人數算是我們的使用者行為條件,所以先不做filter部分,而是我們會希望入住時間與人數到時候是到更詳細的hotelPage去做比對看有沒有能夠容納這些人數與有空房的時間。

useFetch的使用,useEffect的配合

首先先來完成城市的搜尋

所以一樣先利用到的是useFetch,就跟我們用在首頁feature的情況一樣

完整的url應該可以透過下面來抓到我們在nodeApi寫好的getHotels函數

http://localhost:5000/api/v1/hotels?city=高雄

並我們其實希望當他沒有找到任何資料時就主動回傳推薦名單,所以我們可以來更改我們的url,也就是當搜尋欄的city為""空值時,回傳我們的popularHotels列表,展示在我們的hotelsList內。所以我們會用到?:條件句,並因為我們常常要在更改我們的url,所以我拉出來特別宣告,

const city ="台北" //這邊到時候city都會換成useState的destination
const searchUrl =  `/hotels?${city ? "city=" + city : "popularHotel=true" }`
const {data,loading,error} =useFetch(searchUrl)
// console.log(data)

並把資料套到SearchItem上並map出來

{loading ? <>搜尋載入中</> :
  data.map((item,index) =>
    <SearchItem active={index==0 && "active"} 
     key={item._id} dataDetail={item}   conditions={conditions} dates={dates} />
  )
 }

拆分SearchItem框架資料組成

我們之前在列SearchItem UI時,打了很多測試資訊上去,現在要把這些都替換成真實資料,所以我們要先來分析一下,這些資料的類型與傳入方式。

並這邊我們會先處理導入資料進searchItem框架,而"五晚、一位"等資料,想要等我們弄完hotelPage串接時一次把這些價格跟住了幾晚的條件資料一次搞定,現在都先來把原本的資料庫的資料串接上我們的UI。所以回到我們的searchItem.jsx Component來接這些props,


好了後應該會長

設立url的fetchDataUrl state來與useEffect配合

並要來把測試city變數換掉完成真實的useState的destination的input結果


這邊會有一點小複雜,代為的就是能讓我們的fetchData可以在每次按下按鈕就重新搜尋,重新跑一次useFetch,所以用到的是url的變動來帶動整個useFetch中useEffect讓useFetch再跑一次,而每次url的變動與重新宣吿,都希望是跟著按按鈕一起,所以寫在handleClick內。

const searchUrl =  `/hotels?${destination ? "city=" + destination : "popularHotel=true" }`
const [fetchDataUrl,setFetchDataUrl]=useState(searchUrl)//useFetch要能夠重新搜尋就改動url讓他可以重整
const {data,loading,error} =useFetch(fetchDataUrl)

const handleClick = () => {
  dispatch({ type: new_Options, payload: { city: destination, date: dates, options: conditions } })
 setFetchDataUrl(searchUrl)
}

然後useFetch的useEffect也要加上他的dependency

const useFetch = (url) => {
    const [data, setData]=useState([]);
    const [loading,setLoading]=useState(false);
    const [error, setError] =useState("");
    useEffect(()=>{
       const fetchData =async()=>{ 
        setLoading(true);
        try{
            const response = await axios.get(url)
            setData(response.data)
        }catch(err){
            setError(err)
        }
        setLoading(false);
       }
       fetchData()
    },[url]) //這邊有改動加上url
    return {data,loading, error}
}

完成後應該搜尋就會有結果

並在加一進去一些資料連動,然後使用optionsContext的操作資料更新,這邊如果導入得是destinations就會跟我們searchBar一樣快速連動,但我們希望他是跟著搜尋後,在更新title與資料一起顯示,所以我們可以利用的我們context的dispatch,並抓更新過後的context裡面的state,所以就是不想要那麼快連動可以在拉出來多一層的概念。

這邊大致上完成後,來應用day20時那時候教的loading載入畫面,並一樣會用到我們的fetchData中的loading。

skeleton loading的ListItem載入畫面

所以首先我們一樣先處理我們的skeleton loading export是要符合這邊的Searchitem的遮罩樣子。



這邊就不多花太多時間在這邊,附上相關連結
skeleton loading.div github連結
skeleton loading.scss github連結
完成了簡單的地點filter功能後,我們要來加價格搜尋的功能。

最高價格與最低價格搜尋Api

這邊我們既然要增加能夠搜尋最高價格與最低價格的filter功能,首先Api部分我們就要做更改,所以要回到我們Api folder內


//getAllHotels升級版2.0,讓他能抓取全部資料包括新的價格區間也能依照query值去找想要的資料
export const getAllHotels = async(req,res,next)=>{
    const {lowestPrice,highestPrice,...withQuery} = req.query;
    //如果url上有寫popularHotels=true,popularHotels會回傳true 沒有寫就沒這條件
    try{
        const hotelsList = await Hotel.find(
            {
              ...withQuery,
              cheapestPrice:{$gt:lowestPrice || 0,
              $lt:highestPrice || 9999} //這邊一定要這樣打因為涉及到兩個fetch 如果沒有填|| 會出現沒有值的問題
            }
        ).limit(7) //讓他回傳資料最多就七個
        res.status(200).json(hotelsList)
    }catch(error){
        next(errorMessage(500,"無法抓取所有飯店資料",error)) 
    }
}

更改好我們的Api後,這邊要確定能不能運行通常也會去insomnia去檢測,但這邊我們確定ok就一樣先回到我們的client side

const [lowestPrice, setLowestPrice] = useState("");
const [highestPrice, setHighestPrice] = useState("");

const searchUrl = 
 `/hotels?${destination ? "city=" + destination : "popularHotel=true"} &lowestPrice=${lowestPrice} &highestPrice=${highestPrice}`
<div className="listItemLimitPrice">
  <span className="limitTitle">
    每晚最低價格
  </span>
  <input type="text" className='searchInput' onChange={(e)=>setLowestPrice(e.target.value)}/>
</div>
<div className="listItemLimitPrice">
  <span className="limitTitle">
    每晚最高價格
  </span>
  <input type="text" className='searchInput' onChange={(e)=>setHighestPrice(e.target.value)}/>
</div>

最後這樣子就完成了我們的價格區間搜尋機制,

結論

類似的資料爬梳除了要前後端合作去產出外,有時候也要考慮說是前端這處理比較多還是在Api部分處理比較多,這些都會影響到資料回傳的速度,舉例來說,如後台架設之時,常常會遇到很多需要資料爬梳的情況,常見的月營收、平均顧客等等的,後端能現成的Api資料就可以直接抓來使用,但更多是像我們前後端都自身串連,可能就會需要在前端上做函數計算,鐵人賽轉眼間也越來越接近完賽,大概的心得也就是結果不重要,但過程中自身學到了很多,累卻也有成就感,希望能完美的落幕。


上一篇
「全端挑戰」dispatch與payload上傳、讀取操作,contextApi與localstorage的配合
下一篇
「全端挑戰」useLocation.pathname應用,optionsContext資料同步更新、空房情況UI設計與串接room顯示資料
系列文
自己做一個價值幾十萬的動態網站,學會Mern開發、前台UI設計各式觀念與各式Lib、typescript你該學會的前端技術30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言